﻿using System;
using System.Collections.Generic;

namespace _4Gewinnt
{

    public class Node
    {

        // The column the node put its coin
        public int column { get; set; }

        private GameColor MAX;
        private GameColor MIN;

        //sbr: Gewichtung für die Anzahl der Tokens in einer Reihe
        private int[] MAX_ROW = {1, 2, 10, 1000, 1000, 1000, 1000};
        private int[] MIN_ROW = {1, 2, 10, 10000, 10000, 10000, 10000 };

        //sbr: erweitert mit MAX und MIN, da hier für die Score-Berechnung gewusst werden muss, wer ich bin (damit ich bspw. meine Steine zählen kann)
        public Node(int column, GameColor MAX, GameColor MIN)
        {
            this.column = column;
            this.MAX = MAX;
            this.MIN = MIN;
        }

        /// <summary>
        /// Returns the children (game states reachable from the current one) of the node
        /// </summary>
        /// <returns>The child nodes</returns>
        public List<Node> getChildren(RedoBoard state)
        {
            List<Node> children = new List<Node>(7);
            // Create the other game states, which are reachable from this node.
            // There are up to six other states, because there are 7 columns 
            for (int i = 0; i < 7; i++)
            {
                // Check if we can put a coin at column i
                if (state.isLegalColumn(i))
                {
                    children.Add(new Node(i, MAX, MIN));
                }
            }
            return children;
        }

        /// <summary>
        /// Indicates whether the node represents a terminal game state. That is a user finished the game.
        /// </summary>
        /// <param name="board">The game state</param>
        /// <returns></returns>
        public bool isTerminal(RedoBoard state)
        {
            return (state.hasWon(GameColor.Blue) || state.hasWon(GameColor.Red));
        }

        /// <summary>
        /// Returns the calculated score of the current node. The higher the score, the better the state out of the perspective of MAX.
        /// </summary>
        /// <param name="board">The game state</param>
        /// <returns></returns>
        public int getScore(RedoBoard state)
        {
            int myInARow = countMyTokensInARow(state, this.column);
            int opsInARow = countOpsTokensInARow(state, this.column);

            int totalScore = myInARow * MAX_ROW[myInARow-1] + opsInARow * MIN_ROW[opsInARow-1];
            
            Console.WriteLine(this.column + ": " + totalScore);
            return totalScore;
        }

        private int countMyTokensInARow(RedoBoard board, int c)
        {
            return myTokensInARow(board, c);
        }

        private int countOpsTokensInARow(RedoBoard board, int c)
        {
            return opsTokensInARow(board, c);
        }



        /// <summary>
        /// sbr:
        /// Computes the number of token in a row (horizontally, vertically and diagonally) when moving own stone here
        /// </summary>
        /// <param name="c">The column</param>
        /// <param name="board">The board</param>
        /// <returns>The number of tokens I have in a row if moving to column c</returns>
        private int myTokensInARow(RedoBoard board, int c)
        {
            //find accoring row number
            int r = board.findFirstFullRow(c);
            Console.Write(r + " ");

            //if c is illegal column:
            if (r == -1)
                return -1;

            //count stores the maximum number of consecutive tokens in this column
            int count = 0;
            int a = 0;
            int b = 0;

            //search vertical: |
            int countVert = 0;
            a = r - 1;
            b = c;
            while (isMyToken(a, b, board))
            {
                countVert++;
                a--;
            }
            a = r + 1;
            b = c;
            while (isMyToken(a, b, board))
            {
                countVert++;
                a++;
            }
            if (countVert > count)
                count = countVert;

            //search horizontal: -
            int countHor = 0;
            a = r;
            b = c - 1;
            while (isMyToken(a, b, board))
            {
                countHor++;
                b--;
            }
            a = r;
            b = c + 1;
            while (isMyToken(a, b, board))
            {
                countHor++;
                b++;
            }
            if (countHor > count)
                count = countHor;

            //search diagonal1: /
            int countDiag1 = 0;
            a = r + 1;
            b = c - 1;
            while (isMyToken(a, b, board))
            {
                countDiag1++;
                a++;
                b--;
            }
            a = r - 1;
            b = c + 1;
            while (isMyToken(a, b, board))
            {
                countDiag1++;
                a--;
                b++;
            }
            if (countDiag1 > count)
                count = countDiag1;

            //search diagonal2: \
            int countDiag2 = 0;
            a = r + 1;
            b = c + 1;
            while (isMyToken(a, b, board))
            {
                countDiag2++;
                a++;
                b++;
            }
            a = r - 1;
            b = c - 1;
            while (isMyToken(a, b, board))
            {
                countDiag2++;
                a--;
                b--;
            }
            if (countDiag2 > count)
                count = countDiag2;

            return count + 1;
        }

        /// <summary>
        /// sbr:
        /// Computes the number of token in a row (horizontally, vertically and diagonally) when moving a stone here
        /// </summary>
        /// <param name="c">The column</param>
        /// <param name="board">The board</param>
        /// <returns>The number of tokens the opponent has in a row if moving to column c</returns>
        private int opsTokensInARow(RedoBoard board, int c)
        {
            //find accoring row number
            int r = board.findFirstFullRow(c);

            //if c is illegal column:
            if (r == -1)
                return -1;

            //count stores the maximum number of consecutive tokens in this column
            int count = 0;
            int a = 0;
            int b = 0;

            //search vertical: |
            int countVert = 0;
            a = r - 1;
            b = c;
            while (isOpsToken(a, b, board))
            {
                countVert++;
                a--;
            }
            a = r + 1;
            b = c;
            while (isOpsToken(a, b, board))
            {
                countVert++;
                a++;
            }
            if (countVert > count)
                count = countVert;

            //search horizontal: -
            int countHor = 0;
            a = r;
            b = c - 1;
            while (isOpsToken(a, b, board))
            {
                countHor++;
                b--;
            }
            a = r;
            b = c + 1;
            while (isOpsToken(a, b, board))
            {
                countHor++;
                b++;
            }
            if (countHor > count)
                count = countHor;

            //search diagonal1: /
            int countDiag1 = 0;
            a = r + 1;
            b = c - 1;
            while (isOpsToken(a, b, board))
            {
                countDiag1++;
                a++;
                b--;
            }
            a = r - 1;
            b = c + 1;
            while (isOpsToken(a, b, board))
            {
                countDiag1++;
                a--;
                b++;
            }
            if (countDiag1 > count)
                count = countDiag1;

            //search diagonal2: \
            int countDiag2 = 0;
            a = r + 1;
            b = c + 1;
            while (isOpsToken(a, b, board))
            {
                countDiag2++;
                a++;
                b++;
            }
            a = r - 1;
            b = c - 1;
            while (isOpsToken(a, b, board))
            {
                countDiag2++;
                a--;
                b--;
            }
            if (countDiag2 > count)
                count = countDiag2;

            return count + 1;
        }

        /// <summary>
        /// Checks whether my token is on (r,c). 
        /// </summary>
        /// <param name="r">The row</param>
        /// <param name="c">The column</param>
        /// <param name="board">The board</param>
        /// <returns>True, if my token is on (r,c). False, if (r,c) is outside the grid. </returns>
        private bool isMyToken(int r, int c, RedoBoard board)
        {
            if (r >= 0 && r < board.Rows && c >= 0 && c < board.Cols)
            {
                if (board.playerAt(r, c) == MAX)
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Checks whether ops token is on (r,c). 
        /// </summary>
        /// <param name="r">The row</param>
        /// <param name="c">The column</param>
        /// <param name="board">The board</param>
        /// <returns>True, if my token is on (r,c). False, if (r,c) is outside the grid. </returns>
        private bool isOpsToken(int r, int c, RedoBoard board)
        {
            if (r >= 0 && r < board.Rows && c >= 0 && c < board.Cols)
            {
                if (board.playerAt(r, c) == MIN)
                    return true;
            }
            return false;
        }
    }
}
